
#ifndef QLIB_HEADER_QUAD
#define QLIB_HEADER_QUAD

#include <quadtree/Quadtree.h>
#include <entt/entt.hpp>
#include <vector>

#include "../utils/utils.h"
#include "./array.h"
#include "./object.h"

namespace qlib {
class CellQuad {

 public:
  uint32_t number{0};  // 节点数量

  struct Node {
    Node(){};
    Node(const entt::entity& _entity, const quadtree::Box<double>& _box) : entity(_entity), box(_box){};
    Node(uint32_t _parent_id, const entt::entity& _entity, const quadtree::Box<double>& _box) : parent_id(_parent_id), entity(_entity), box(_box){};

    quadtree::Box<double> box;
    entt::entity entity{0};
    uint32_t parent_id{0};

    bool operator==(const Node& rhs) const { return rhs.entity == entity; }
  };

  struct GetBox {
    quadtree::Box<double> operator()(const Node& node) const { return node.box; }
  };

  typedef std::shared_ptr<quadtree::Quadtree<Node, GetBox, std::equal_to<Node>, double>> s_ptr;

  // CellQuad() {};
  // ~CellQuad() {};

  static s_ptr create(const quadtree::Box<double>& box) {
    return std::make_shared<quadtree::Quadtree<Node, GetBox, std::equal_to<Node>, double>>(box);
  }

  static s_ptr create() {
    quadtree::Box<double> box(-1e9f, -1e9f, 1e18f, 1e18f);
    return std::make_shared<quadtree::Quadtree<Node, GetBox, std::equal_to<Node>, double>>(box);
  }

  using NodeArray = std::vector<Node>;
};

// 几何部分选择四叉树
class GeoPartQuad {

 public:
  struct Node {
    Node(){};
    Node(int32_t _id, const quadtree::Box<double>& _box) : id(_id), box(_box){};
    Node(int32_t _id, GeoPartType _type, const quadtree::Box<double>& _box) : id(_id), type(_type), box(_box){};

    quadtree::Box<double> box;
    int32_t id;
    GeoPartType type;
    Vec2d point;
    Vec2dArray edge;

    bool operator==(const Node& rhs) const { return rhs.type == type && rhs.id == id; }

    void setPoint(const Vec2d& _point) { point = _point; }
    void setEdge(const Vec2dArray& _edge) { edge = _edge; }
  };

  struct GetBox {
    quadtree::Box<double> operator()(const Node& node) const { return node.box; }
  };

  typedef std::shared_ptr<quadtree::Quadtree<Node, GetBox, std::equal_to<Node>, double>> s_ptr;

  // GeoPartQuad() {};
  // ~GeoPartQuad() {};

  static s_ptr create(const quadtree::Box<double>& box) {
    return std::make_shared<quadtree::Quadtree<Node, GetBox, std::equal_to<Node>, double>>(box);
  }
  static s_ptr create() {
    quadtree::Box<double> box(-1e9f, -1e9f, 1e18f, 1e18f);
    return std::make_shared<quadtree::Quadtree<Node, GetBox, std::equal_to<Node>, double>>(box);
  }

  static void add_points(GeoPartQuad::s_ptr s_quad, const Vec2dArray& points, const GeoPartType& type) {
    // 顶点
    auto w = 1;
    uint32_t id = 0;
    for (auto& p : points) {
      Vec2dArray ab = {Vec2d{p.x - w, p.y - w}, Vec2d{p.x + w, p.y + w}};
      quadtree::Box<double> nbox(ab[0].x, ab[0].y, ab[1].x - ab[0].x, ab[1].y - ab[0].y);
      auto node = GeoPartQuad::Node(id, type, nbox);
      node.setPoint(p);
      s_quad->add(node);
      id++;
    }
  }

  static void add_edges(GeoPartQuad::s_ptr s_quad, const std::vector<Vec2dArray>& edges, const GeoPartType& type) {
    // 顶点
    uint32_t id = 0;
    for (auto& eg : edges) {
      auto ab = get_box(eg);
      quadtree::Box<double> nbox(ab[0].x, ab[0].y, ab[1].x - ab[0].x, ab[1].y - ab[0].y);
      auto node = GeoPartQuad::Node(id, type, nbox);
      node.setEdge(eg);
      s_quad->add(node);
      id++;
    }
  }
};

}  // namespace qlib

#endif